<?php
// +----------------------------------------------------------------------
// | 双擎基础框架 [ 基于ThinkLibrary扩展 ]
// +----------------------------------------------------------------------
// | 感谢ThinkLibrary原作者Anyon的封装
// +----------------------------------------------------------------------
// | Copyright (c) 2022~2024 https://www.sqm.la All rights reserved.
// +----------------------------------------------------------------------
// | Author: rotoos <87291030@qq.com>
// +----------------------------------------------------------------------
namespace think\admin\service;

use think\admin\Library;
use think\admin\model\SysMerchant;
use think\admin\model\SysRelation;
use think\admin\model\SysRole;
use think\admin\Service;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;

/**
 * 关系服务
 * Class RelationService
 * @package think\admin\service
 */
class RelationService extends Service
{
    /**
     * 查询用户与租户关系获取租户ID
     * @param int $userId
     * @return string
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function getUserByTenantId(int $userId): string
    {
        // 如果用户ID无效，直接返回 '-1'
        if (!$userId) {
            return '-1';
        }

        // 检查会话中是否已经存在用户信息
        $sessionKey = "user_{$userId}";
        $userSession = Library::$sapp->session->get($sessionKey);

        if ($userSession && isset($userSession['tenantId'])) {
            return $userSession['tenantId'];
        }

        // 会话中不存在用户信息，执行查询
        $conditions = [
            [
                'field' => 'object_id',
                'category' => 'SYS_USER_HAS_TENANT',
                'returnField' => 'target_id'
            ],
            [
                'field' => 'target_id',
                'category' => 'SYS_TENANT_HAS_USER',
                'returnField' => 'object_id'
            ]
        ];
        $tenantId = SysRelation::getRelatedId($userId, $conditions);

        // 如果找到租户ID，存储到会话并返回
        if ($tenantId) {
            Library::$sapp->session->set($sessionKey, ['userId' => $userId, 'tenantId' => (string)$tenantId]);
            return (string)$tenantId;
        }

        // 检查用户是否为商户，并获取商户的租户ID
        [$state, $merchantId] = static::isMerchant($userId);

        if ($state) {
            $tenantId = static::merchantHasTenant($merchantId);
            Library::$sapp->session->set($sessionKey, ['userId' => $userId, 'tenantId' => $tenantId]);
            return $tenantId;
        }

        // 所有尝试都失败，返回 '-1'
        return '-1';
    }


    /**
     * 查询用户与商户关系获取商户ID
     * @param int $userId
     * @return string
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function getUserByMerchantId(int $userId): string
    {
        // 如果用户ID无效，直接返回 '-1'
        if (!$userId) {
            return '-1';
        }

        // 检查会话中是否已经存在商户信息
        $sessionKey = "merchantId_{$userId}";
        $merchantSession = Library::$sapp->session->get($sessionKey);

        if ($merchantSession && isset($merchantSession['merchantId'])) {
            return $merchantSession['merchantId'];
        }

        // 会话中不存在商户信息，执行查询
        $conditions = [
            [
                'field' => 'object_id',
                'category' => 'SYS_USER_HAS_MERCHANT',
                'returnField' => 'target_id'
            ],
            [
                'field' => 'target_id',
                'category' => 'SYS_MERCHANT_HAS_USER',
                'returnField' => 'object_id'
            ]
        ];
        $merchantId = SysRelation::getRelatedId($userId, $conditions);

        // 如果找到商户ID，存储到会话并返回
        if ($merchantId) {
            Library::$sapp->session->set($sessionKey, ['userId' => $userId, 'merchantId' => (string)$merchantId]);
            return (string)$merchantId;
        }

        // 如果未找到商户ID，返回 '-1'
        return '-1';
    }


    /**
     * 查询用户与商户关系获取商户
     * @param int $userId
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function getUserByMerchant(int $userId): array
    {
        // 用户ID不存在返回空数组
        if (!$userId) return [];

        $sessionKey = "merchant_{$userId}";
        $cachedMerchant = Library::$sapp->session->get($sessionKey);

        // 如果会话中存在缓存的商户信息，直接返回
        if ($cachedMerchant) {
            return $cachedMerchant;
        }


        $merchant = []; // 初始化商户数组
        $roleType = static::getUserByRoleType($userId);

        if ($roleType === 'GLOBAL') {
            // 如果是全局账号的显示全部,默认使用第一个商家作为默认数据
            $merchants = SysMerchant::mk()->where(['is_deleted' => 0, 'status' => 0])->select()->toArray();
            if ($merchants) {
                $merchant = ['self' => $merchants[0]['merchantId'] ?? null] + $merchants;
            }
        } else {
            // 获取租户ID
            $tenantId = static::getUserByTenantId($userId);
            if ($tenantId !== '-1') {
                if (AdminService::isTenantAdmin()) {
                    // 租户管理员获取关联的商家信息
                    $merchantData = SysRelation::mk()->where(['category' => 'SYS_TENANT_HAS_MERCHANT', 'object_id' => $tenantId])->column('ext_json');
                    $merchants = array_map(function ($json) {
                        return json_decode($json, true); // 将第二个参数设置为 true 以获取关联数组
                    }, $merchantData);
                    if ($merchants) {
                        $merchant = ['self' => $merchants[0]['merchantId'] ?? null] + $merchants;
                    }
                } elseif (AdminService::isMerchantAdmin()) {
                    // 商户管理员获取关联的商家信息
                    $merchantData = SysRelation::mk()->where(['object_id' => $userId, 'category' => 'SYS_USER_HAS_MERCHANT'])->value('ext_json');
                    if ($merchantData) {
                        $merchant = json_decode($merchantData, true);
                        // 检查解码是否成功，并且确保 $merchant 是一个数组
                        if (is_array($merchant)) {
                            // 如果 $merchant 是一个二维数组（多个商户），取第一个商户的 'merchantId'
                            // 否则直接取 'merchantId'（单个商户）
                            $firstMerchant = $merchant[0] ?? $merchant;

                            // 检查确保 'merchantId' 存在
                            if (isset($firstMerchant['merchantId'])) {
                                $merchant['self'] = $firstMerchant['merchantId'];
                            } else {
                                // 'merchantId' 不存在，处理错误或设置默认值
                                $merchant['self'] = '-1';
                            }
                        } else {
                            // 解码失败或者不是数组，处理错误或设置默认值
                            $merchant = [];
                            $merchant['self'] = '-1';
                        }
                    } else {
                        // 反查商户关联用户，取到商户ID
                        $merchantId = SysRelation::mk()->where(['target_id' => $userId, 'category' => 'SYS_MERCHANT_HAS_USER'])->value('object_id');
                        if ($merchantId) {
                            $merchant = SysMerchant::mk()->where(['merchant_id' => $merchantId, 'is_deleted' => 0, 'status' => 0])->findOrEmpty()->toArray();
                            $merchant['self'] = $merchant['merchantId'];
                        }
                    }
                }
            }
        }
        // 缓存商户信息到会话中
        Library::$sapp->session->set($sessionKey, $merchant);
        return $merchant;
    }


    /**
     * 用户与角色类型的关系
     * @param int $userId
     * @return string
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function getUserByRoleType(int $userId): string
    {
        // 如果用户ID无效，直接返回空字符串
        if (!$userId) {
            return '';
        }

        $sessionKey = "roleType_{$userId}";
        $cachedRoleType = Library::$sapp->session->get($sessionKey);

        // 如果会话中存在缓存的角色类型，直接返回
        if ($cachedRoleType !== null) {
            return $cachedRoleType;
        }

        // 如果用户是超级管理员，返回 'GLOBAL'
        if (AdminService::isSuper()) {
            Library::$sapp->session->set($sessionKey, 'GLOBAL');
            return 'GLOBAL';
        }

        $conditions = [
            [
                'field' => 'object_id',
                'category' => 'SYS_USER_HAS_ROLE',
                'returnField' => 'target_id'
            ],
            [
                'field' => 'target_id',
                'category' => 'SYS_ROLE_HAS_USER',
                'returnField' => 'object_id'
            ]
        ];
        $relation = SysRelation::getRelatedColumn($userId, $conditions);

        // 如果仍然没有找到关联的角色，返回空字符串
        if (empty($relation)) {
            return '';
        }

        // 如果用户有多个角色，检查是否包含 'GLOBAL' 类型
        if (count($relation) > 1) {
            $roles = SysRole::mk()->whereIn('id', $relation)
                ->where(['is_deleted' => 0, 'status' => 0])
                ->column('category');
            $roleType = in_array('GLOBAL', $roles) ? 'GLOBAL' : 'BIZ';
            Library::$sapp->session->set($sessionKey, $roleType);
            return $roleType;
        }

        // 用户只有一个角色，直接返回该角色的类型
        $roleType = SysRole::mk()->where([
            'id' => $relation[0] ?? null,
            'is_deleted' => 0,
            'status' => 0
        ])->value('category');

        // 缓存角色类型到会话
        Library::$sapp->session->set($sessionKey, $roleType);

        return $roleType;
    }



    /**
     * 用户是否属于租户管理员及租户标识
     * @param int $userId
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function isTenant(int $userId): array
    {
        $sessionKey = "isTenant_{$userId}";
        $cachedTenant = Library::$sapp->session->get($sessionKey);

        // 如果会话中存在缓存的租户信息，直接返回
        if ($cachedTenant !== null) {
            return $cachedTenant;
        }

        $conditions = [
            [
                'field' => 'object_id',
                'category' => 'SYS_USER_HAS_TENANT',
                'returnField' => 'target_id'
            ],
            [
                'field' => 'target_id',
                'category' => 'SYS_TENANT_HAS_USER',
                'returnField' => 'object_id'
            ]
        ];
        $tenantId = SysRelation::getRelatedId($userId, $conditions);

        // 缓存租户信息到会话
        $result = $tenantId ? [true, $tenantId] : [false, ''];
        Library::$sapp->session->set($sessionKey, $result);

        return $result;
    }


    /**
     * 查询租户与商户关系获取商户列表
     * @param int $userId
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function getUserByMerchantList(int $userId): array
    {
        // 用户ID不存在返回空数组
        if (!$userId) {
            return [];
        }

        $sessionKey = "merchantList_{$userId}";
        $cachedMerchantList = Library::$sapp->session->get($sessionKey);

        // 如果会话中存在缓存的商户列表，直接返回
        if ($cachedMerchantList !== null) {
            return $cachedMerchantList;
        }

        // 如果是全局账号，获取所有商户
        if (static::getUserByRoleType($userId) === 'GLOBAL') {
            $merchants = SysMerchant::mk()
                ->where(['is_deleted' => 0, 'status' => 0])
                ->select()
                ->toArray();
            Library::$sapp->session->set($sessionKey, $merchants);
            return $merchants;
        }

        // 获取用户关联的租户ID
        $tenantId = static::getUserByTenantId($userId);

        // 如果用户不是全局账号，但是有租户ID
        if ($tenantId !== '-1') {
            if (AdminService::isTenantAdmin()) {
                // 如果是租户管理员，获取租户关联的所有商户
                $merchants = static::getMerchantsByTenant($tenantId);
                Library::$sapp->session->set($sessionKey, $merchants);
                return $merchants;
            }

            if (AdminService::isMerchantAdmin()) {
                // 如果是商户管理员，获取用户关联的商户
                $merchants = static::getMerchantsByUser($userId);
                Library::$sapp->session->set($sessionKey, $merchants);
                return $merchants;
            }
        }

        // 如果既不是全局账号，也不是租户或商户管理员，返回空数组
        Library::$sapp->session->set($sessionKey, []);
        return [];
    }

    /**
     * 商户下管理员的分类
     * @param int $userId
     * @return array|mixed|SysRole|\think\Model
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function getMerchantSubRole(int $userId): array
    {
        $sessionKey = "merchantSubRole_{$userId}";
        $cachedSubRole = Library::$sapp->session->get($sessionKey);

        // 如果会话中存在缓存的子角色信息，直接返回
        if ($cachedSubRole !== null) {
            return $cachedSubRole;
        }

        $conditions = [
            [
                'field' => 'object_id',
                'category' => 'SYS_USER_HAS_ROLE',
                'returnField' => 'target_id'
            ],
            [
                'field' => 'target_id',
                'category' => 'SYS_ROLE_HAS_USER',
                'returnField' => 'object_id'
            ]
        ];
        $roleId = SysRelation::getRelatedId($userId, $conditions);

        if (!$roleId) {
            Library::$sapp->session->set($sessionKey, []);
            return [];
        }

        $subRole = SysRole::mk()->where(['id' => $roleId])->field('id,name,code')->findOrEmpty()->toArray();

        // 缓存子角色信息到会话
        Library::$sapp->session->set($sessionKey, $subRole);

        return $subRole;
    }

    /**
     * @param $tenantId
     * @return array
     */
    private static function getMerchantsByTenant($tenantId): array
    {
        $sessionKey = "merchantsByTenant_{$tenantId}";
        $cachedMerchants = Library::$sapp->session->get($sessionKey);

        // 如果会话中存在缓存的商户信息，直接返回
        if ($cachedMerchants !== null) {
            return $cachedMerchants;
        }

        $merchantData = SysRelation::mk()
            ->where(['category' => 'SYS_TENANT_HAS_MERCHANT', 'object_id' => $tenantId])
            ->column('ext_json');

        $merchants = array_map('json_decode', $merchantData, array_fill(0, count($merchantData), true));

        // 缓存商户信息到会话
        Library::$sapp->session->set($sessionKey, $merchants);

        return $merchants;
    }

    /**
     * @param $userId
     * @return array
     */
    private static function getMerchantsByUser($userId): array
    {
        $sessionKey = "merchantsByUser_{$userId}";
        $cachedMerchants = Library::$sapp->session->get($sessionKey);

        // 如果会话中存在缓存的商户信息，直接返回
        if ($cachedMerchants !== null) {
            return $cachedMerchants;
        }

        $merchants = SysRelation::mk()
            ->where(function($query) use ($userId) {
                $query->where(['object_id' => $userId, 'category' => 'SYS_USER_HAS_MERCHANT'])
                    ->whereOr(['target_id' => $userId, 'category' => 'SYS_MERCHANT_HAS_USER']);
            })
            ->field('ext_json, object_id')
            ->findOrEmpty()
            ->toArray();

        if ($merchants) {
            // 如果 ext_json 存在，直接返回解码后的结果
            if (!empty($merchants['ext_json'])) {
                $result = json_decode($merchants['ext_json'], true);
                Library::$sapp->session->set($sessionKey, $result);
                return $result;
            }

            // 如果没有 ext_json，使用 object_id 作为 merchantId 查询商户信息
            $merchantId = $merchants['object_id'];
            $result = SysMerchant::mk()
                ->where(['merchant_id' => $merchantId, 'is_deleted' => 0, 'status' => 0])
                ->field('id,merchant_id as merchantId,name')
                ->findOrEmpty()
                ->toArray();

            Library::$sapp->session->set($sessionKey, $result);
            return $result;
        }

        // 缓存空结果
        Library::$sapp->session->set($sessionKey, []);
        return [];
    }


    /**
     * 租户商户关联
     * @param string $merchantId
     * @return string|null
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function merchantHasTenant(string $merchantId): ?string
    {
        $sessionKey = "merchantHasTenant_{$merchantId}";
        $cachedTenantId = Library::$sapp->session->get($sessionKey);

        // 如果会话中存在缓存的租户ID，直接返回
        if ($cachedTenantId !== null) {
            return $cachedTenantId;
        }

        $conditions = [
            [
                'field' => 'object_id',
                'category' => 'SYS_MERCHANT_HAS_TENANT',
                'returnField' => 'target_id'
            ],
            [
                'field' => 'target_id',
                'category' => 'SYS_TENANT_HAS_MERCHANT',
                'returnField' => 'object_id'
            ]
        ];

        $tenantId = SysRelation::getRelatedId($merchantId, $conditions);

        // 缓存租户ID到会话
        Library::$sapp->session->set($sessionKey, $tenantId);

        return $tenantId;
    }



    /**
     * 用户是否属于商家管理员及商家标识
     * @param int $userId
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function isMerchant(int $userId): array
    {
        $sessionKey = "isMerchant_{$userId}";
        $cachedResult = Library::$sapp->session->get($sessionKey);

        // 如果会话中存在缓存的结果，直接返回
        if ($cachedResult !== null) {
            return $cachedResult;
        }

        $conditions = [
            [
                'field' => 'object_id',
                'category' => 'SYS_USER_HAS_MERCHANT',
                'returnField' => 'target_id'
            ],
            [
                'field' => 'target_id',
                'category' => 'SYS_MERCHANT_HAS_USER',
                'returnField' => 'object_id'
            ]
        ];

        $merchantId = SysRelation::getRelatedId($userId, $conditions);
        $result = $merchantId ? [true, $merchantId] : [false, ''];

        // 缓存结果到会话
        Library::$sapp->session->set($sessionKey, $result);

        return $result;
    }

}